if(NOT DEFINED CUDA_TARGET_ARCHS)
    message(FATAL_ERROR "Variable CUDA_TARGET_ARCHS must be set at this point")
endif()

function(cross_list list filter output)
    set(cross "")
    # message(STATUS "filter [${${list}}] to only include [${${filter}}]")
    foreach(elem IN LISTS ${list})
        # message(STATUS "checking for ${elem}")
        if(${elem} IN_LIST ${filter})
            # message(STATUS "found! append ${elem} to  [${cross}]")
            list(APPEND cross ${elem})
        endif()
    endforeach()
    set(${output} ${cross} PARENT_SCOPE)
endfunction(cross_list)


function(set_cuda_arch_ptx_flags_nvcc arch_list output)
    set(flags "")
    foreach(arch IN LISTS ${arch_list})
        set(flags "${flags} --generate-code arch=compute_${arch},code=compute_${arch}")
    endforeach()
    set(${output} ${flags} PARENT_SCOPE)
endfunction(set_cuda_arch_ptx_flags_nvcc)

function(set_cuda_arch_flags_nvcc arch_list output)
    set(flags "")
    foreach(arch IN LISTS ${arch_list})
        set(flags "${flags} --generate-code arch=compute_${arch},code=sm_${arch}")
    endforeach()
    set(${output} ${flags} PARENT_SCOPE)
endfunction(set_cuda_arch_flags_nvcc)

function(set_cuda_arch_flags_clang arch_list output)
    set(flags "")
    foreach(arch IN LISTS ${arch_list})
        set(flags "${flags} --cuda-gpu-arch=sm_${arch}")
    endforeach()
    set(${output} ${flags} PARENT_SCOPE)
endfunction(set_cuda_arch_flags_clang)

function(set_cuda_arch_flags arch_list output)
    if(CMAKE_CUDA_COMPILER_ID STREQUAL NVIDIA)
        set_cuda_arch_flags_nvcc("${arch_list}" flags)
    elseif(CMAKE_CUDA_COMPILER_ID MATCHES CLANG)
        set_cuda_arch_flags_clang("${arch_list}" flags)
    else()
        message(ERROR "unsupported CUDA compiler ${CMAKE_CUDA_COMPILER_ID}.")
    endif()
    set(${output} ${flags} PARENT_SCOPE)
endfunction(set_cuda_arch_flags)


function(set_cuda_ptx_flags arch_list output)
    if(CMAKE_CUDA_COMPILER_ID STREQUAL NVIDIA)
        set_cuda_arch_ptx_flags_nvcc("${arch_list}" flags)
    else()
        message(ERROR "unsupported CUDA compiler ${CMAKE_CUDA_COMPILER_ID}.")
     endif()
    set(${output} ${flags} PARENT_SCOPE)
endfunction(set_cuda_ptx_flags)

function(add_sources_cuda_archs list cuda_architectures_list cuda_ptx_list)
    # message(STATUS "adding source files files: ${ARGN} for archs ${cuda_architectures_list}")
    cross_list(cuda_architectures_list CUDA_TARGET_ARCHS _filtered_architectures_list)
    if(_filtered_architectures_list)
        set_cuda_arch_flags(_filtered_architectures_list _arch_flags)
        foreach(file ${ARGN})
            # message(STATUS "added: ${file} for archs ${_arch_flags}")
            list(APPEND ${list} ${file})
            set_property(SOURCE ${file} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_arch_flags}")
            set_property(SOURCE ${file} PROPERTY CUDA_ARCHS ${_filtered_architectures_list})
        endforeach()
    endif()

    # message(STATUS "adding source files files: ${ARGN} for archs ${cuda_ptx_list}")
    set_cuda_ptx_flags(cuda_ptx_list _ptx_flags)
    foreach(file ${ARGN})
           # message(STATUS "added: ${file} for archs ${_ptx_flags}")
        list(APPEND ${list} ${file})
        set_property(SOURCE ${file} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_ptx_flags}")
        set_property(SOURCE ${file} PROPERTY CUDA_ARCHS ${_filtered_architectures_list})
    endforeach()

    set(${list} ${${list}} PARENT_SCOPE)
endfunction(add_sources_cuda_archs)
